This notebook defines the most focal recurrent copy number units by removing focal changes that are within entire chromosome arm losses and gains. Most focal here meaning:

  • If a chromosome arm is not clearly defined as a gain or loss (and is callable) we look to define the cytoband level status
  • If a cytoband is not clearly defined as a gain or loss (and is callable) we then look to define the gene-level status

Usage

This notebook is intended to be run from the command line with the following (assumes you are in the root directory of the repository):

Rscript -e "rmarkdown::render('analyses/focal-cn-file-preparation/05-define-most-focal-cn-units.Rmd', clean = TRUE)"

Cutoffs:

# The percentage of calls a particular status needs to be 
# above to be called the majority status -- the decision
# for a cutoff of 90% here was made to ensure that the status
# is not only the majority status but it is also significantly
# called more than the other status values in the region
percent_threshold <- 0.9

# The percentage threshold for determining if enough of a region
# (arm, cytoband, or gene) is callable to determine its status --
# the decision for a cutoff of 50% here was made as it seems reasonable
# to expect a region to be more than 50% callable for a dominant status
# call to be made
uncallable_threshold <- 0.5

Set up

Libraries and functions

library(tidyverse)
── Attaching packages ────────────────────────────────────────────────────────────────────────── tidyverse 1.2.1 ──
✔ ggplot2 3.2.0     ✔ purrr   0.3.2
✔ tibble  2.1.3     ✔ dplyr   0.8.3
✔ tidyr   0.8.3     ✔ stringr 1.4.0
✔ readr   1.3.1     ✔ forcats 0.4.0
── Conflicts ───────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()

Custom Function

status_majority_caller <- function(status_df,
                                   region_variable,
                                   status_column_name,
                                   id_column_name,
                                   percent_threshold_val, 
                                   uncallable_threshold_val = uncallable_threshold) {
  # Given a data.frame with cytoband/gene-level copy number status data,
  # find the dominant status of the cytoband/gene region by calculating
  # the percentages of the region that each call represents.
  #
  # Args:
  #   status_df: data.frame with cytoband/gene-level copy number status data
  #   region_variable: string of the column name/region to calculate copy number
  #                    status percentages for
  #   status_column_name: string of the column name that holds the relevant copy
  #                       number status data
  #   id_column_name: string of the column name that holds the sample id values
  #   percent_threshold_val: What percent of calls a particular status needs to be 
  #                      above to be called the majority. 
  #   uncallable_threshold_val: a threshold for determining if enough of a region is 
  #                         callable to determine its status. 
  #   
  # Return:
  #   status_count: data.frame with percentage values for each unique status in
  #                 each unique region (arm/cytoband/gene) and the dominant
  #                 status for that region
  
  # Tidyeval for these columns 
  region_sym <- rlang::sym(region_variable)
  status_sym <- rlang::sym(status_column_name)
  id_sym <- rlang::sym(id_column_name)
  
  # Format the data and group it
  status_df <- status_df %>%
    # Keep only the columns we need
    select(!!id_sym, !!region_sym, !!status_sym) %>%
    # Group by status, sample, and region
    group_by(!!id_sym, !!region_sym, !!status_sym) %>%
    # Get a total count of each s
    summarize(status_n = n())
  
  # Getting total counts by region 
  region_total_df <- status_df %>%
    # Group by region and for each sample
    group_by(!!region_sym, !!id_sym) %>%
    # Get totals
    summarize(total = sum(status_n))
  
  # Calculate percent by using the region totals
  status_percent_df <- status_df %>%
    ungroup() %>% 
    # Tack on the by region totals
    inner_join(region_total_df, by = c(region_variable, id_column_name)) %>% 
    # Bring back our region variable as its own column
    mutate(percent = status_n / total) %>% 
    # Don't need these columns now
    select(-status_n, -total) %>% 
    # Spread to individual columns
    spread(status_column_name, percent) %>%
    # Turn NAs into 0s
    replace_na(list(
      gain = 0,
      loss = 0,
      neutral = 0,
      amplification = 0,
      uncallable = 0,
      unstable = 0
    ))

  # The logic here is to define the region status based on the majority of calls
  # in the region -- if the number of calls for a specific status is 
  # responsible for more than `percent_threshold` value of the total calls in that
  # region, then it is used to define the region's status (exception is for the 
  # uncallable status where we define the region as uncallable when more than the
  # `uncallable_threshold` of the calls in that region are uncallable)
  if ((region_variable == "chromosome_arm") | (region_variable == "gene_symbol")) {
    status_percent_df <- status_percent_df %>%
      mutate(
        dominant_status = case_when(
          gain > percent_threshold_val ~ "gain",
          loss > percent_threshold_val ~ "loss",
          amplification > percent_threshold_val ~ "amplification",
          TRUE ~ "neutral"
        )
      )
  } else if (region_variable == "cytoband") {
    status_percent_df <- status_percent_df %>%
      mutate(
        dominant_status = case_when(
          uncallable > uncallable_threshold_val ~ "uncallable",
          gain > percent_threshold_val ~ "gain",
          loss > percent_threshold_val ~ "loss",
          neutral > percent_threshold_val ~ "neutral",
          TRUE ~ "unstable"
        )
      )
  }

  return(status_percent_df)
}
plot_dominant_status_calls <- function(status_count_df,
                                       region_variable) {
  # Given a data.frame with the percentage values for each region and the
  # dominant status for that region, plot the dominant status call on the
  # x-axis with the percentage values on the y-axis.
  #
  # Args:
  #   status_count_df: data.frame with percentage values for each unique status
  #                    in each unique region (arm/cytoband/gene) and the
  #                    dominant status for that region
  #   region_variable: string of the region (which will also be a column name)
  #                    that the data.frame holds percentage values for
  # Return:
  #   status_plot: plot representing the dominant status call on the x-axis and
  #                the percentage values on the y-axis
  
  status_count_df %>%
    # Remove the columns we don't want to plot
    select(-region_variable, -biospecimen_id) %>%
    dplyr::ungroup() %>%
    # Gather the data.frame to have columns and values in the format of
    # our status call, the percentage of total calls that status call is
    # responisble for, and the dominant status call made based on the
    # percentage value
    tidyr::gather(status, percent,-dominant_status) %>%
    # Plot our focal status values on the x-axis and the percentage values
    # on the y-axis
    ggplot2::ggplot(ggplot2::aes(x = status,
                                 y = percent)) +
    ggplot2::geom_jitter() +
    # Facet wrap around each dominant status value
    ggplot2::facet_wrap( ~ dominant_status)
}

Files and directories

results_dir <- "results"

# Define a logical object for running in CI
running_in_ci <- params$is_ci

Read in files

Read in cytoband status file and format it for what we will need in this notebook.

# Read in the file with consensus CN status data and the UCSC cytoband data --
# generated in `03-add-cytoband-status-consensus.Rmd`
consensus_seg_cytoband_status_df <-
  read_tsv(file.path("results", "consensus_seg_with_ucsc_cytoband_status.tsv.gz")) %>%
  # Need this to not have `chr`
  mutate(chr = gsub("chr", "", chr),
         cytoband = paste0(chr, cytoband)) %>%
  select(
    chromosome_arm,
    # Distinguish this dominant status that is based on cytobands, from the status 
    dominant_cytoband_status = dominant_status,
    cytoband,
    Kids_First_Biospecimen_ID
  )
Parsed with column specification:
cols(
  Kids_First_Biospecimen_ID = col_character(),
  chr = col_character(),
  cytoband = col_character(),
  dominant_status = col_character(),
  band_length = col_double(),
  callable_fraction = col_double(),
  gain_fraction = col_double(),
  loss_fraction = col_double(),
  chromosome_arm = col_character()
)

Read in the chromosome-level and gene-level data.

# Read in the annotated CN file (without the UCSC data)
consensus_seg_autosomes_df <-
  read_tsv(file.path(results_dir, "consensus_seg_annotated_cn_autosomes.tsv.gz"))
Parsed with column specification:
cols(
  biospecimen_id = col_character(),
  status = col_character(),
  copy_number = col_double(),
  ploidy = col_double(),
  ensembl = col_character(),
  gene_symbol = col_character(),
  cytoband = col_character()
)

Joining the gene-level, cytoband-level, and arm-level data into one data frame.

combined_status_df <- consensus_seg_autosomes_df %>%
  inner_join(
    consensus_seg_cytoband_status_df,
    by = c("biospecimen_id" = "Kids_First_Biospecimen_ID", "cytoband")
  )

Define most focal units

Determine chromosome arm status

# Use our custom function to make the status calls
arm_status_count <- status_majority_caller(
  combined_status_df,
  "chromosome_arm",
  "status", 
  "biospecimen_id",
  percent_threshold_val = percent_threshold
)

# Display table
arm_status_count %>%
  group_by(dominant_status)

These are the chromosome arms that have not been defined as gain or loss – we want to define their cytoband/gene-level status

# Let's get a vector of the neutral arms
neutral_arms <- arm_status_count %>%
  filter(dominant_status == "neutral") %>% 
  dplyr::pull("chromosome_arm")

Determine cytoband status

We want to include cytoband and gene-level calls for chromosome arms that have not been defined as a gain or loss.

# Filter the annotated CN data to include only neutral chromosome arms
neutral_status_arm_df <- combined_status_df %>%
  filter(chromosome_arm %in% neutral_arms)

Making cytoband-level majority calls.

if (!(running_in_ci)) {
# Now count the cytoband level calls (for each status call) and define
# the cytoband as that status if more than 50% of the total counts are
# for that particular status
cytoband_status_count <- status_majority_caller(
  neutral_status_arm_df,
  "cytoband",
  "dominant_cytoband_status",
  "biospecimen_id",
  percent_threshold_val = percent_threshold
)

# Display table
cytoband_status_count %>%
  group_by(dominant_status)
}

Determine gene-level status

Plot calls

Plot the final dominant status call on the x-axis and the percent of each status on the y-axis.

Plot chromosome arm status calls

rr # Plot if not running in circleCI if (!(running_in_ci)) { plot_dominant_status_calls(gene_status_count, _symbol) }

Plot cytoband status calls

# Run `plot_dominant_status_calls` function for the cytoband calls if not
# running in CI
if (!(running_in_ci)) {
  plot_dominant_status_calls(cytoband_status_count,
                             "cytoband")
}

Plot gene status calls

# Plot if not running in circleCI
if (!(running_in_ci)) {
  plot_dominant_status_calls(gene_status_count,
                             "gene_symbol")
}

Combine arm, cytoband, and gene-level status data

Transform data into long format

Tranform the chromosome arm status data

Transform the cytoband status data

Transform the gene-level status data

if (!(running_in_ci)) {
  final_gene_status_df <- combined_status_count_df %>%
    # Filter to only neutral chromosome arms and cytobands
    # that are NA or unstable (do not have a clear gain or loss call)
    filter(dominant_status.arm == "neutral",
           dominant_status.cytoband == c("NA", "unstable")) %>%
    select(
      Kids_First_Biospecimen_ID = biospecimen_id,
      region = gene_symbol,
      status = dominant_status
    ) %>%
    distinct()
}

Combine status data for all regions

Spread status data

Session Info

sessionInfo()
R version 3.6.0 (2019-04-26)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Debian GNU/Linux 9 (stretch)

Matrix products: default
BLAS/LAPACK: /usr/lib/libopenblasp-r0.2.19.so

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C               LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
 [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=C              LC_PAPER=en_US.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C             LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] forcats_0.4.0   stringr_1.4.0   dplyr_0.8.3     purrr_0.3.2     readr_1.3.1     tidyr_0.8.3     tibble_2.1.3   
[8] ggplot2_3.2.0   tidyverse_1.2.1

loaded via a namespace (and not attached):
 [1] Rcpp_1.0.1       cellranger_1.1.0 pillar_1.4.2     compiler_3.6.0   tools_3.6.0      jsonlite_1.6    
 [7] lubridate_1.7.4  nlme_3.1-140     gtable_0.3.0     lattice_0.20-38  pkgconfig_2.0.2  rlang_0.4.0     
[13] cli_1.1.0        rstudioapi_0.10  yaml_2.2.0       haven_2.1.1      xfun_0.8         withr_2.1.2     
[19] xml2_1.2.0       httr_1.4.0       knitr_1.23       generics_0.0.2   hms_0.4.2        grid_3.6.0      
[25] tidyselect_0.2.5 glue_1.3.1       R6_2.4.0         readxl_1.3.1     modelr_0.1.4     magrittr_1.5    
[31] backports_1.1.4  scales_1.0.0     rvest_0.3.4      assertthat_0.2.1 colorspace_1.4-1 stringi_1.4.3   
[37] lazyeval_0.2.2   munsell_0.5.0    broom_0.5.2      crayon_1.3.4    
LS0tCnRpdGxlOiAiRmluZCBtb3N0IGZvY2FsIHJlY3VycmVudCBjb3B5IG51bWJlciB1bml0cyIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jOiBUUlVFCiAgICB0b2NfZmxvYXQ6IFRSVUUKYXV0aG9yOiBDaGFudGUgQmV0aGVsbCBmb3IgQUxTRiBDQ0RMCmRhdGU6IDIwMjAKcGFyYW1zOgogIGlzX2NpOiBGQUxTRQotLS0KClRoaXMgbm90ZWJvb2sgZGVmaW5lcyB0aGUgbW9zdCBmb2NhbCByZWN1cnJlbnQgY29weSBudW1iZXIgdW5pdHMgYnkgcmVtb3ZpbmcgZm9jYWwgY2hhbmdlcyB0aGF0IGFyZSB3aXRoaW4gZW50aXJlIGNocm9tb3NvbWUgYXJtIGxvc3NlcyBhbmQgZ2FpbnMuCl9Nb3N0IGZvY2FsXyBoZXJlIG1lYW5pbmc6CgotIElmIGEgY2hyb21vc29tZSBhcm0gaXMgbm90IGNsZWFybHkgZGVmaW5lZCBhcyBhIGdhaW4gb3IgbG9zcyAoYW5kIGlzIGNhbGxhYmxlKSB3ZSBsb29rIHRvIGRlZmluZSB0aGUgY3l0b2JhbmQgbGV2ZWwgc3RhdHVzCi0gSWYgYSBjeXRvYmFuZCBpcyBub3QgY2xlYXJseSBkZWZpbmVkIGFzIGEgZ2FpbiBvciBsb3NzIChhbmQgaXMgY2FsbGFibGUpIHdlIHRoZW4gbG9vayB0byBkZWZpbmUgdGhlIGdlbmUtbGV2ZWwgc3RhdHVzCgojIyBVc2FnZQoKVGhpcyBub3RlYm9vayBpcyBpbnRlbmRlZCB0byBiZSBydW4gZnJvbSB0aGUgY29tbWFuZCBsaW5lIHdpdGggdGhlIGZvbGxvd2luZyAoYXNzdW1lcyB5b3UgYXJlIGluIHRoZSByb290IGRpcmVjdG9yeSBvZiB0aGUgcmVwb3NpdG9yeSk6CgpgYGAKUnNjcmlwdCAtZSAicm1hcmtkb3duOjpyZW5kZXIoJ2FuYWx5c2VzL2ZvY2FsLWNuLWZpbGUtcHJlcGFyYXRpb24vMDUtZGVmaW5lLW1vc3QtZm9jYWwtY24tdW5pdHMuUm1kJywgY2xlYW4gPSBUUlVFKSIKYGBgCgojIyMgQ3V0b2ZmczogCgpgYGB7cn0KIyBUaGUgcGVyY2VudGFnZSBvZiBjYWxscyBhIHBhcnRpY3VsYXIgc3RhdHVzIG5lZWRzIHRvIGJlIAojIGFib3ZlIHRvIGJlIGNhbGxlZCB0aGUgbWFqb3JpdHkgc3RhdHVzIC0tIHRoZSBkZWNpc2lvbgojIGZvciBhIGN1dG9mZiBvZiA5MCUgaGVyZSB3YXMgbWFkZSB0byBlbnN1cmUgdGhhdCB0aGUgc3RhdHVzCiMgaXMgbm90IG9ubHkgdGhlIG1ham9yaXR5IHN0YXR1cyBidXQgaXQgaXMgYWxzbyBzaWduaWZpY2FudGx5CiMgY2FsbGVkIG1vcmUgdGhhbiB0aGUgb3RoZXIgc3RhdHVzIHZhbHVlcyBpbiB0aGUgcmVnaW9uCnBlcmNlbnRfdGhyZXNob2xkIDwtIDAuOQoKIyBUaGUgcGVyY2VudGFnZSB0aHJlc2hvbGQgZm9yIGRldGVybWluaW5nIGlmIGVub3VnaCBvZiBhIHJlZ2lvbgojIChhcm0sIGN5dG9iYW5kLCBvciBnZW5lKSBpcyBjYWxsYWJsZSB0byBkZXRlcm1pbmUgaXRzIHN0YXR1cyAtLQojIHRoZSBkZWNpc2lvbiBmb3IgYSBjdXRvZmYgb2YgNTAlIGhlcmUgd2FzIG1hZGUgYXMgaXQgc2VlbXMgcmVhc29uYWJsZQojIHRvIGV4cGVjdCBhIHJlZ2lvbiB0byBiZSBtb3JlIHRoYW4gNTAlIGNhbGxhYmxlIGZvciBhIGRvbWluYW50IHN0YXR1cwojIGNhbGwgdG8gYmUgbWFkZQp1bmNhbGxhYmxlX3RocmVzaG9sZCA8LSAwLjUKYGBgCgojIyBTZXQgdXAKCiMjIyBMaWJyYXJpZXMgYW5kIGZ1bmN0aW9ucwoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCiMjIyBDdXN0b20gRnVuY3Rpb24KCmBgYHtyfQpzdGF0dXNfbWFqb3JpdHlfY2FsbGVyIDwtIGZ1bmN0aW9uKHN0YXR1c19kZiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWdpb25fdmFyaWFibGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RhdHVzX2NvbHVtbl9uYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlkX2NvbHVtbl9uYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBlcmNlbnRfdGhyZXNob2xkX3ZhbCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdW5jYWxsYWJsZV90aHJlc2hvbGRfdmFsID0gdW5jYWxsYWJsZV90aHJlc2hvbGQpIHsKICAjIEdpdmVuIGEgZGF0YS5mcmFtZSB3aXRoIGN5dG9iYW5kL2dlbmUtbGV2ZWwgY29weSBudW1iZXIgc3RhdHVzIGRhdGEsCiAgIyBmaW5kIHRoZSBkb21pbmFudCBzdGF0dXMgb2YgdGhlIGN5dG9iYW5kL2dlbmUgcmVnaW9uIGJ5IGNhbGN1bGF0aW5nCiAgIyB0aGUgcGVyY2VudGFnZXMgb2YgdGhlIHJlZ2lvbiB0aGF0IGVhY2ggY2FsbCByZXByZXNlbnRzLgogICMKICAjIEFyZ3M6CiAgIyAgIHN0YXR1c19kZjogZGF0YS5mcmFtZSB3aXRoIGN5dG9iYW5kL2dlbmUtbGV2ZWwgY29weSBudW1iZXIgc3RhdHVzIGRhdGEKICAjICAgcmVnaW9uX3ZhcmlhYmxlOiBzdHJpbmcgb2YgdGhlIGNvbHVtbiBuYW1lL3JlZ2lvbiB0byBjYWxjdWxhdGUgY29weSBudW1iZXIKICAjICAgICAgICAgICAgICAgICAgICBzdGF0dXMgcGVyY2VudGFnZXMgZm9yCiAgIyAgIHN0YXR1c19jb2x1bW5fbmFtZTogc3RyaW5nIG9mIHRoZSBjb2x1bW4gbmFtZSB0aGF0IGhvbGRzIHRoZSByZWxldmFudCBjb3B5CiAgIyAgICAgICAgICAgICAgICAgICAgICAgbnVtYmVyIHN0YXR1cyBkYXRhCiAgIyAgIGlkX2NvbHVtbl9uYW1lOiBzdHJpbmcgb2YgdGhlIGNvbHVtbiBuYW1lIHRoYXQgaG9sZHMgdGhlIHNhbXBsZSBpZCB2YWx1ZXMKICAjICAgcGVyY2VudF90aHJlc2hvbGRfdmFsOiBXaGF0IHBlcmNlbnQgb2YgY2FsbHMgYSBwYXJ0aWN1bGFyIHN0YXR1cyBuZWVkcyB0byBiZSAKICAjICAgICAgICAgICAgICAgICAgICAgIGFib3ZlIHRvIGJlIGNhbGxlZCB0aGUgbWFqb3JpdHkuIAogICMgICB1bmNhbGxhYmxlX3RocmVzaG9sZF92YWw6IGEgdGhyZXNob2xkIGZvciBkZXRlcm1pbmluZyBpZiBlbm91Z2ggb2YgYSByZWdpb24gaXMgCiAgIyAgICAgICAgICAgICAgICAgICAgICAgICBjYWxsYWJsZSB0byBkZXRlcm1pbmUgaXRzIHN0YXR1cy4gCiAgIyAgIAogICMgUmV0dXJuOgogICMgICBzdGF0dXNfY291bnQ6IGRhdGEuZnJhbWUgd2l0aCBwZXJjZW50YWdlIHZhbHVlcyBmb3IgZWFjaCB1bmlxdWUgc3RhdHVzIGluCiAgIyAgICAgICAgICAgICAgICAgZWFjaCB1bmlxdWUgcmVnaW9uIChhcm0vY3l0b2JhbmQvZ2VuZSkgYW5kIHRoZSBkb21pbmFudAogICMgICAgICAgICAgICAgICAgIHN0YXR1cyBmb3IgdGhhdCByZWdpb24KICAKICAjIFRpZHlldmFsIGZvciB0aGVzZSBjb2x1bW5zIAogIHJlZ2lvbl9zeW0gPC0gcmxhbmc6OnN5bShyZWdpb25fdmFyaWFibGUpCiAgc3RhdHVzX3N5bSA8LSBybGFuZzo6c3ltKHN0YXR1c19jb2x1bW5fbmFtZSkKICBpZF9zeW0gPC0gcmxhbmc6OnN5bShpZF9jb2x1bW5fbmFtZSkKICAKICAjIEZvcm1hdCB0aGUgZGF0YSBhbmQgZ3JvdXAgaXQKICBzdGF0dXNfZGYgPC0gc3RhdHVzX2RmICU+JQogICAgIyBLZWVwIG9ubHkgdGhlIGNvbHVtbnMgd2UgbmVlZAogICAgc2VsZWN0KCEhaWRfc3ltLCAhIXJlZ2lvbl9zeW0sICEhc3RhdHVzX3N5bSkgJT4lCiAgICAjIEdyb3VwIGJ5IHN0YXR1cywgc2FtcGxlLCBhbmQgcmVnaW9uCiAgICBncm91cF9ieSghIWlkX3N5bSwgISFyZWdpb25fc3ltLCAhIXN0YXR1c19zeW0pICU+JQogICAgIyBHZXQgYSB0b3RhbCBjb3VudCBvZiBlYWNoIHMKICAgIHN1bW1hcml6ZShzdGF0dXNfbiA9IG4oKSkKICAKICAjIEdldHRpbmcgdG90YWwgY291bnRzIGJ5IHJlZ2lvbiAKICByZWdpb25fdG90YWxfZGYgPC0gc3RhdHVzX2RmICU+JQogICAgIyBHcm91cCBieSByZWdpb24gYW5kIGZvciBlYWNoIHNhbXBsZQogICAgZ3JvdXBfYnkoISFyZWdpb25fc3ltLCAhIWlkX3N5bSkgJT4lCiAgICAjIEdldCB0b3RhbHMKICAgIHN1bW1hcml6ZSh0b3RhbCA9IHN1bShzdGF0dXNfbikpCiAgCiAgIyBDYWxjdWxhdGUgcGVyY2VudCBieSB1c2luZyB0aGUgcmVnaW9uIHRvdGFscwogIHN0YXR1c19wZXJjZW50X2RmIDwtIHN0YXR1c19kZiAlPiUKICAgIHVuZ3JvdXAoKSAlPiUgCiAgICAjIFRhY2sgb24gdGhlIGJ5IHJlZ2lvbiB0b3RhbHMKICAgIGlubmVyX2pvaW4ocmVnaW9uX3RvdGFsX2RmLCBieSA9IGMocmVnaW9uX3ZhcmlhYmxlLCBpZF9jb2x1bW5fbmFtZSkpICU+JSAKICAgICMgQnJpbmcgYmFjayBvdXIgcmVnaW9uIHZhcmlhYmxlIGFzIGl0cyBvd24gY29sdW1uCiAgICBtdXRhdGUocGVyY2VudCA9IHN0YXR1c19uIC8gdG90YWwpICU+JSAKICAgICMgRG9uJ3QgbmVlZCB0aGVzZSBjb2x1bW5zIG5vdwogICAgc2VsZWN0KC1zdGF0dXNfbiwgLXRvdGFsKSAlPiUgCiAgICAjIFNwcmVhZCB0byBpbmRpdmlkdWFsIGNvbHVtbnMKICAgIHNwcmVhZChzdGF0dXNfY29sdW1uX25hbWUsIHBlcmNlbnQpICU+JQogICAgIyBUdXJuIE5BcyBpbnRvIDBzCiAgICByZXBsYWNlX25hKGxpc3QoCiAgICAgIGdhaW4gPSAwLAogICAgICBsb3NzID0gMCwKICAgICAgbmV1dHJhbCA9IDAsCiAgICAgIGFtcGxpZmljYXRpb24gPSAwLAogICAgICB1bmNhbGxhYmxlID0gMCwKICAgICAgdW5zdGFibGUgPSAwCiAgICApKQoKICAjIFRoZSBsb2dpYyBoZXJlIGlzIHRvIGRlZmluZSB0aGUgcmVnaW9uIHN0YXR1cyBiYXNlZCBvbiB0aGUgbWFqb3JpdHkgb2YgY2FsbHMKICAjIGluIHRoZSByZWdpb24gLS0gaWYgdGhlIG51bWJlciBvZiBjYWxscyBmb3IgYSBzcGVjaWZpYyBzdGF0dXMgaXMgCiAgIyByZXNwb25zaWJsZSBmb3IgbW9yZSB0aGFuIGBwZXJjZW50X3RocmVzaG9sZGAgdmFsdWUgb2YgdGhlIHRvdGFsIGNhbGxzIGluIHRoYXQKICAjIHJlZ2lvbiwgdGhlbiBpdCBpcyB1c2VkIHRvIGRlZmluZSB0aGUgcmVnaW9uJ3Mgc3RhdHVzIChleGNlcHRpb24gaXMgZm9yIHRoZSAKICAjIHVuY2FsbGFibGUgc3RhdHVzIHdoZXJlIHdlIGRlZmluZSB0aGUgcmVnaW9uIGFzIHVuY2FsbGFibGUgd2hlbiBtb3JlIHRoYW4gdGhlCiAgIyBgdW5jYWxsYWJsZV90aHJlc2hvbGRgIG9mIHRoZSBjYWxscyBpbiB0aGF0IHJlZ2lvbiBhcmUgdW5jYWxsYWJsZSkKICBpZiAoKHJlZ2lvbl92YXJpYWJsZSA9PSAiY2hyb21vc29tZV9hcm0iKSB8IChyZWdpb25fdmFyaWFibGUgPT0gImdlbmVfc3ltYm9sIikpIHsKICAgIHN0YXR1c19wZXJjZW50X2RmIDwtIHN0YXR1c19wZXJjZW50X2RmICU+JQogICAgICBtdXRhdGUoCiAgICAgICAgZG9taW5hbnRfc3RhdHVzID0gY2FzZV93aGVuKAogICAgICAgICAgZ2FpbiA+IHBlcmNlbnRfdGhyZXNob2xkX3ZhbCB+ICJnYWluIiwKICAgICAgICAgIGxvc3MgPiBwZXJjZW50X3RocmVzaG9sZF92YWwgfiAibG9zcyIsCiAgICAgICAgICBhbXBsaWZpY2F0aW9uID4gcGVyY2VudF90aHJlc2hvbGRfdmFsIH4gImFtcGxpZmljYXRpb24iLAogICAgICAgICAgVFJVRSB+ICJuZXV0cmFsIgogICAgICAgICkKICAgICAgKQogIH0gZWxzZSBpZiAocmVnaW9uX3ZhcmlhYmxlID09ICJjeXRvYmFuZCIpIHsKICAgIHN0YXR1c19wZXJjZW50X2RmIDwtIHN0YXR1c19wZXJjZW50X2RmICU+JQogICAgICBtdXRhdGUoCiAgICAgICAgZG9taW5hbnRfc3RhdHVzID0gY2FzZV93aGVuKAogICAgICAgICAgdW5jYWxsYWJsZSA+IHVuY2FsbGFibGVfdGhyZXNob2xkX3ZhbCB+ICJ1bmNhbGxhYmxlIiwKICAgICAgICAgIGdhaW4gPiBwZXJjZW50X3RocmVzaG9sZF92YWwgfiAiZ2FpbiIsCiAgICAgICAgICBsb3NzID4gcGVyY2VudF90aHJlc2hvbGRfdmFsIH4gImxvc3MiLAogICAgICAgICAgbmV1dHJhbCA+IHBlcmNlbnRfdGhyZXNob2xkX3ZhbCB+ICJuZXV0cmFsIiwKICAgICAgICAgIFRSVUUgfiAidW5zdGFibGUiCiAgICAgICAgKQogICAgICApCiAgfQoKICByZXR1cm4oc3RhdHVzX3BlcmNlbnRfZGYpCn0KYGBgCgpgYGB7cn0KcGxvdF9kb21pbmFudF9zdGF0dXNfY2FsbHMgPC0gZnVuY3Rpb24oc3RhdHVzX2NvdW50X2RmLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWdpb25fdmFyaWFibGUpIHsKICAjIEdpdmVuIGEgZGF0YS5mcmFtZSB3aXRoIHRoZSBwZXJjZW50YWdlIHZhbHVlcyBmb3IgZWFjaCByZWdpb24gYW5kIHRoZQogICMgZG9taW5hbnQgc3RhdHVzIGZvciB0aGF0IHJlZ2lvbiwgcGxvdCB0aGUgZG9taW5hbnQgc3RhdHVzIGNhbGwgb24gdGhlCiAgIyB4LWF4aXMgd2l0aCB0aGUgcGVyY2VudGFnZSB2YWx1ZXMgb24gdGhlIHktYXhpcy4KICAjCiAgIyBBcmdzOgogICMgICBzdGF0dXNfY291bnRfZGY6IGRhdGEuZnJhbWUgd2l0aCBwZXJjZW50YWdlIHZhbHVlcyBmb3IgZWFjaCB1bmlxdWUgc3RhdHVzCiAgIyAgICAgICAgICAgICAgICAgICAgaW4gZWFjaCB1bmlxdWUgcmVnaW9uIChhcm0vY3l0b2JhbmQvZ2VuZSkgYW5kIHRoZQogICMgICAgICAgICAgICAgICAgICAgIGRvbWluYW50IHN0YXR1cyBmb3IgdGhhdCByZWdpb24KICAjICAgcmVnaW9uX3ZhcmlhYmxlOiBzdHJpbmcgb2YgdGhlIHJlZ2lvbiAod2hpY2ggd2lsbCBhbHNvIGJlIGEgY29sdW1uIG5hbWUpCiAgIyAgICAgICAgICAgICAgICAgICAgdGhhdCB0aGUgZGF0YS5mcmFtZSBob2xkcyBwZXJjZW50YWdlIHZhbHVlcyBmb3IKICAjIFJldHVybjoKICAjICAgc3RhdHVzX3Bsb3Q6IHBsb3QgcmVwcmVzZW50aW5nIHRoZSBkb21pbmFudCBzdGF0dXMgY2FsbCBvbiB0aGUgeC1heGlzIGFuZAogICMgICAgICAgICAgICAgICAgdGhlIHBlcmNlbnRhZ2UgdmFsdWVzIG9uIHRoZSB5LWF4aXMKICAKICBzdGF0dXNfY291bnRfZGYgJT4lCiAgICAjIFJlbW92ZSB0aGUgY29sdW1ucyB3ZSBkb24ndCB3YW50IHRvIHBsb3QKICAgIHNlbGVjdCgtcmVnaW9uX3ZhcmlhYmxlLCAtYmlvc3BlY2ltZW5faWQpICU+JQogICAgZHBseXI6OnVuZ3JvdXAoKSAlPiUKICAgICMgR2F0aGVyIHRoZSBkYXRhLmZyYW1lIHRvIGhhdmUgY29sdW1ucyBhbmQgdmFsdWVzIGluIHRoZSBmb3JtYXQgb2YKICAgICMgb3VyIHN0YXR1cyBjYWxsLCB0aGUgcGVyY2VudGFnZSBvZiB0b3RhbCBjYWxscyB0aGF0IHN0YXR1cyBjYWxsIGlzCiAgICAjIHJlc3BvbmlzYmxlIGZvciwgYW5kIHRoZSBkb21pbmFudCBzdGF0dXMgY2FsbCBtYWRlIGJhc2VkIG9uIHRoZQogICAgIyBwZXJjZW50YWdlIHZhbHVlCiAgICB0aWR5cjo6Z2F0aGVyKHN0YXR1cywgcGVyY2VudCwtZG9taW5hbnRfc3RhdHVzKSAlPiUKICAgICMgUGxvdCBvdXIgZm9jYWwgc3RhdHVzIHZhbHVlcyBvbiB0aGUgeC1heGlzIGFuZCB0aGUgcGVyY2VudGFnZSB2YWx1ZXMKICAgICMgb24gdGhlIHktYXhpcwogICAgZ2dwbG90Mjo6Z2dwbG90KGdncGxvdDI6OmFlcyh4ID0gc3RhdHVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gcGVyY2VudCkpICsKICAgIGdncGxvdDI6Omdlb21faml0dGVyKCkgKwogICAgIyBGYWNldCB3cmFwIGFyb3VuZCBlYWNoIGRvbWluYW50IHN0YXR1cyB2YWx1ZQogICAgZ2dwbG90Mjo6ZmFjZXRfd3JhcCggfiBkb21pbmFudF9zdGF0dXMpCn0KYGBgCgoKIyMjIEZpbGVzIGFuZCBkaXJlY3RvcmllcwoKYGBge3J9CnJlc3VsdHNfZGlyIDwtICJyZXN1bHRzIgoKIyBEZWZpbmUgYSBsb2dpY2FsIG9iamVjdCBmb3IgcnVubmluZyBpbiBDSQpydW5uaW5nX2luX2NpIDwtIHBhcmFtcyRpc19jaQpgYGAKCiMjIyBSZWFkIGluIGZpbGVzCgpSZWFkIGluIGN5dG9iYW5kIHN0YXR1cyBmaWxlIGFuZCBmb3JtYXQgaXQgZm9yIHdoYXQgd2Ugd2lsbCBuZWVkIGluIHRoaXMgbm90ZWJvb2suIAoKYGBge3J9CiMgUmVhZCBpbiB0aGUgZmlsZSB3aXRoIGNvbnNlbnN1cyBDTiBzdGF0dXMgZGF0YSBhbmQgdGhlIFVDU0MgY3l0b2JhbmQgZGF0YSAtLQojIGdlbmVyYXRlZCBpbiBgMDMtYWRkLWN5dG9iYW5kLXN0YXR1cy1jb25zZW5zdXMuUm1kYApjb25zZW5zdXNfc2VnX2N5dG9iYW5kX3N0YXR1c19kZiA8LQogIHJlYWRfdHN2KGZpbGUucGF0aCgicmVzdWx0cyIsICJjb25zZW5zdXNfc2VnX3dpdGhfdWNzY19jeXRvYmFuZF9zdGF0dXMudHN2Lmd6IikpICU+JQogICMgTmVlZCB0aGlzIHRvIG5vdCBoYXZlIGBjaHJgCiAgbXV0YXRlKGNociA9IGdzdWIoImNociIsICIiLCBjaHIpLAogICAgICAgICBjeXRvYmFuZCA9IHBhc3RlMChjaHIsIGN5dG9iYW5kKSkgJT4lCiAgc2VsZWN0KAogICAgY2hyb21vc29tZV9hcm0sCiAgICAjIERpc3Rpbmd1aXNoIHRoaXMgZG9taW5hbnQgc3RhdHVzIHRoYXQgaXMgYmFzZWQgb24gY3l0b2JhbmRzLCBmcm9tIHRoZSBzdGF0dXMgCiAgICBkb21pbmFudF9jeXRvYmFuZF9zdGF0dXMgPSBkb21pbmFudF9zdGF0dXMsCiAgICBjeXRvYmFuZCwKICAgIEtpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQKICApCmBgYAoKUmVhZCBpbiB0aGUgY2hyb21vc29tZS1sZXZlbCBhbmQgZ2VuZS1sZXZlbCBkYXRhLiAKCmBgYHtyfQojIFJlYWQgaW4gdGhlIGFubm90YXRlZCBDTiBmaWxlICh3aXRob3V0IHRoZSBVQ1NDIGRhdGEpCmNvbnNlbnN1c19zZWdfYXV0b3NvbWVzX2RmIDwtCiAgcmVhZF90c3YoZmlsZS5wYXRoKHJlc3VsdHNfZGlyLCAiY29uc2Vuc3VzX3NlZ19hbm5vdGF0ZWRfY25fYXV0b3NvbWVzLnRzdi5neiIpKQpgYGAKCkpvaW5pbmcgdGhlIGdlbmUtbGV2ZWwsIGN5dG9iYW5kLWxldmVsLCBhbmQgYXJtLWxldmVsIGRhdGEgaW50byBvbmUgZGF0YSBmcmFtZS4KCmBgYHtyfQpjb21iaW5lZF9zdGF0dXNfZGYgPC0gY29uc2Vuc3VzX3NlZ19hdXRvc29tZXNfZGYgJT4lCiAgaW5uZXJfam9pbigKICAgIGNvbnNlbnN1c19zZWdfY3l0b2JhbmRfc3RhdHVzX2RmLAogICAgYnkgPSBjKCJiaW9zcGVjaW1lbl9pZCIgPSAiS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCIsICJjeXRvYmFuZCIpCiAgKQpgYGAKCiMjIERlZmluZSBtb3N0IGZvY2FsIHVuaXRzCgojIyMgRGV0ZXJtaW5lIGNocm9tb3NvbWUgYXJtIHN0YXR1cwoKYGBge3J9CiMgVXNlIG91ciBjdXN0b20gZnVuY3Rpb24gdG8gbWFrZSB0aGUgc3RhdHVzIGNhbGxzCmFybV9zdGF0dXNfY291bnQgPC0gc3RhdHVzX21ham9yaXR5X2NhbGxlcigKICBjb21iaW5lZF9zdGF0dXNfZGYsCiAgImNocm9tb3NvbWVfYXJtIiwKICAic3RhdHVzIiwgCiAgImJpb3NwZWNpbWVuX2lkIiwKICBwZXJjZW50X3RocmVzaG9sZF92YWwgPSBwZXJjZW50X3RocmVzaG9sZAopCgojIERpc3BsYXkgdGFibGUKYXJtX3N0YXR1c19jb3VudCAlPiUKICBncm91cF9ieShkb21pbmFudF9zdGF0dXMpCmBgYAoKVGhlc2UgYXJlIHRoZSBjaHJvbW9zb21lIGFybXMgdGhhdCBoYXZlIG5vdCBiZWVuIGRlZmluZWQgYXMgZ2FpbiBvciBsb3NzIC0tIHdlIHdhbnQgdG8gZGVmaW5lIHRoZWlyIGN5dG9iYW5kL2dlbmUtbGV2ZWwgc3RhdHVzCgpgYGB7cn0KIyBMZXQncyBnZXQgYSB2ZWN0b3Igb2YgdGhlIG5ldXRyYWwgYXJtcwpuZXV0cmFsX2FybXMgPC0gYXJtX3N0YXR1c19jb3VudCAlPiUKICBmaWx0ZXIoZG9taW5hbnRfc3RhdHVzID09ICJuZXV0cmFsIikgJT4lIAogIGRwbHlyOjpwdWxsKCJjaHJvbW9zb21lX2FybSIpCmBgYAoKIyMjIERldGVybWluZSBjeXRvYmFuZCBzdGF0dXMKCldlIHdhbnQgdG8gaW5jbHVkZSBjeXRvYmFuZCBhbmQgZ2VuZS1sZXZlbCBjYWxscyBmb3IgY2hyb21vc29tZSBhcm1zIHRoYXQgaGF2ZSBub3QgYmVlbiBkZWZpbmVkIGFzIGEgZ2FpbiBvciBsb3NzLgoKYGBge3J9CiMgRmlsdGVyIHRoZSBhbm5vdGF0ZWQgQ04gZGF0YSB0byBpbmNsdWRlIG9ubHkgbmV1dHJhbCBjaHJvbW9zb21lIGFybXMKbmV1dHJhbF9zdGF0dXNfYXJtX2RmIDwtIGNvbWJpbmVkX3N0YXR1c19kZiAlPiUKICBmaWx0ZXIoY2hyb21vc29tZV9hcm0gJWluJSBuZXV0cmFsX2FybXMpCmBgYAoKTWFraW5nIGN5dG9iYW5kLWxldmVsIG1ham9yaXR5IGNhbGxzLiAKCmBgYHtyfQppZiAoIShydW5uaW5nX2luX2NpKSkgewojIE5vdyBjb3VudCB0aGUgY3l0b2JhbmQgbGV2ZWwgY2FsbHMgKGZvciBlYWNoIHN0YXR1cyBjYWxsKSBhbmQgZGVmaW5lCiMgdGhlIGN5dG9iYW5kIGFzIHRoYXQgc3RhdHVzIGlmIG1vcmUgdGhhbiA1MCUgb2YgdGhlIHRvdGFsIGNvdW50cyBhcmUKIyBmb3IgdGhhdCBwYXJ0aWN1bGFyIHN0YXR1cwpjeXRvYmFuZF9zdGF0dXNfY291bnQgPC0gc3RhdHVzX21ham9yaXR5X2NhbGxlcigKICBuZXV0cmFsX3N0YXR1c19hcm1fZGYsCiAgImN5dG9iYW5kIiwKICAiZG9taW5hbnRfY3l0b2JhbmRfc3RhdHVzIiwKICAiYmlvc3BlY2ltZW5faWQiLAogIHBlcmNlbnRfdGhyZXNob2xkX3ZhbCA9IHBlcmNlbnRfdGhyZXNob2xkCikKCiMgRGlzcGxheSB0YWJsZQpjeXRvYmFuZF9zdGF0dXNfY291bnQgJT4lCiAgZ3JvdXBfYnkoZG9taW5hbnRfc3RhdHVzKQp9CmBgYAoKIyMjIERldGVybWluZSBnZW5lLWxldmVsIHN0YXR1cwoKYGBge3J9CmlmICghKHJ1bm5pbmdfaW5fY2kpKSB7CiAgIyBUaGVzZSBhcmUgdGhlIGN5dG9iYW5kcyB0aGF0IGhhdmUgbm90IGJlZW4gZGVmaW5lZCBhcyBnYWluIG9yIGxvc3MgLS0KICAjIHdlIHdhbnQgdG8gZGVmaW5lIHRoZWlyIGdlbmUtbGV2ZWwgc3RhdHVzCiAgbmV1dHJhbF9jeXRvYmFuZHMgPC0gY3l0b2JhbmRfc3RhdHVzX2NvdW50ICU+JQogICAgZmlsdGVyKGRvbWluYW50X3N0YXR1cyAlaW4lIGMoInVuc3RhYmxlIiwgIk5BIikpICU+JQogICAgZHBseXI6OnB1bGwoImN5dG9iYW5kIikKCiAgIyBGaWx0ZXIgdGhlIGFubm90YXRlZCBDTiBkYXRhIHRvIGluY2x1ZGUgb25seSB0aGVzZSBjeXRvYmFuZHMKICBuZXV0cmFsX3N0YXR1c19jeXRvYmFuZF9kZiA8LSBjb21iaW5lZF9zdGF0dXNfZGYgJT4lCiAgICBmaWx0ZXIoY3l0b2JhbmQgJWluJSBuZXV0cmFsX2N5dG9iYW5kcykKICAKICAjIE5vdyBjb3VudCB0aGUgZ2VuZS1sZXZlbCBjYWxscyAoZm9yIGVhY2ggc3RhdHVzIGNhbGwpIGFuZCBkZWZpbmUKICAjIHRoZSBnZW5lIGFzIHRoYXQgc3RhdHVzIGlmIG1vcmUgdGhhbiA1MCUgb2YgdGhlIHRvdGFsIGNvdW50cyBhcmUKICAjIGZvciB0aGF0IHBhcnRpY3VsYXIgc3RhdHVzCiAgZ2VuZV9zdGF0dXNfY291bnQgPC0gc3RhdHVzX21ham9yaXR5X2NhbGxlcihuZXV0cmFsX3N0YXR1c19jeXRvYmFuZF9kZiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJnZW5lX3N5bWJvbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3RhdHVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJiaW9zcGVjaW1lbl9pZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwZXJjZW50X3RocmVzaG9sZF92YWwgPSBwZXJjZW50X3RocmVzaG9sZCkKICAKICAjIERpc3BsYXkgdGFibGUKICBnZW5lX3N0YXR1c19jb3VudAp9CmBgYAoKIyMgUGxvdCBjYWxscwoKUGxvdCB0aGUgZmluYWwgZG9taW5hbnQgc3RhdHVzIGNhbGwgb24gdGhlIHgtYXhpcyBhbmQgdGhlIHBlcmNlbnQgb2YgZWFjaCBzdGF0dXMgb24gdGhlIHktYXhpcy4KCiMjIyBQbG90IGNocm9tb3NvbWUgYXJtIHN0YXR1cyBjYWxscwoKYGBge3J9CiMgUnVuIGBwbG90X2RvbWluYW50X3N0YXR1c19jYWxsc2AgZnVuY3Rpb24gZm9yIHRoZSBjaHJvbW9zb21lIGFybSBjYWxscwpwbG90X2RvbWluYW50X3N0YXR1c19jYWxscygKICBhcm1fc3RhdHVzX2NvdW50LAogICJjaHJvbW9zb21lX2FybSIKKQpgYGAKCiMjIyBQbG90IGN5dG9iYW5kIHN0YXR1cyBjYWxscwoKYGBge3IgZmlnLndpZHRoID0gMTB9CiMgUnVuIGBwbG90X2RvbWluYW50X3N0YXR1c19jYWxsc2AgZnVuY3Rpb24gZm9yIHRoZSBjeXRvYmFuZCBjYWxscyBpZiBub3QKIyBydW5uaW5nIGluIENJCmlmICghKHJ1bm5pbmdfaW5fY2kpKSB7CiAgcGxvdF9kb21pbmFudF9zdGF0dXNfY2FsbHMoY3l0b2JhbmRfc3RhdHVzX2NvdW50LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjeXRvYmFuZCIpCn0KYGBgCgojIyMgUGxvdCBnZW5lIHN0YXR1cyBjYWxscwoKYGBge3J9CiMgUGxvdCBpZiBub3QgcnVubmluZyBpbiBjaXJjbGVDSQppZiAoIShydW5uaW5nX2luX2NpKSkgewogIHBsb3RfZG9taW5hbnRfc3RhdHVzX2NhbGxzKGdlbmVfc3RhdHVzX2NvdW50LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJnZW5lX3N5bWJvbCIpCn0KYGBgCgojIyBDb21iaW5lIGFybSwgY3l0b2JhbmQsIGFuZCBnZW5lLWxldmVsIHN0YXR1cyBkYXRhCgpgYGB7cn0KIyBUaGUgbG9naWMgdmFyaWFibGUgYHJ1bm5pbmdfaW5fY2lgIGlzIG5lZWRlZCBoZXJlIGJlY2F1c2UgdGhlIENJIHRlc3RpbmcKIyBmaWxlcyBkbyBub3QgY29udGFpbiBhbnkgb2YgdGhlIGdlbmVzIGluIHRoZSBgZ2VuZV9zdGF0dXNfY291bnRgIGRhdGEuZnJhbWUKIyBnZW5lcmF0ZWQgYWJvdmUgKHdoZW4gYHJ1bm5pbmdfaW5fY2lgID09IEZBTFNFKQppZiAoIShydW5uaW5nX2luX2NpKSkgewogIAogICMgTXV0YXRlIGEgY2hyb21vc29tZSBhcm0gY29sdW1uIHZhcmlhYmxlIGZvciBqb2luaW5nIHdpdGggdGhlIGNocm9tb3NvbWUKICAjIGFybSBkYXRhCiAgY3l0b2JhbmRfc3RhdHVzX2NvdW50IDwtIGN5dG9iYW5kX3N0YXR1c19jb3VudCAlPiUKICAgIG11dGF0ZShjaHJvbW9zb21lX2FybSA9IGdzdWIoIihwfHEpLioiLCAiXFwxIiwgY3l0b2JhbmQpKQogIAogICMgQ29tYmluZSB0aGUgYXJtLCBjeXRvYmFuZCwgYW5kIGdlbmUgc3RhdHVzIGNvdW50IGRhdGEKICBjb21iaW5lZF9zdGF0dXNfY291bnRfZGYgPC0gYXJtX3N0YXR1c19jb3VudCAlPiUKICAgIGlubmVyX2pvaW4oY3l0b2JhbmRfc3RhdHVzX2NvdW50LAogICAgICAgICAgICAgIGJ5ID0gYygiYmlvc3BlY2ltZW5faWQiLCAiY2hyb21vc29tZV9hcm0iKSwKICAgICAgICAgICAgICBzdWZmaXggPSBjKCIuYXJtIiwgIi5jeXRvYmFuZCIpKSAlPiUKICAgIGlubmVyX2pvaW4oZ2VuZV9zdGF0dXNfY291bnQsCiAgICAgICAgICAgICAgYnkgPSAiYmlvc3BlY2ltZW5faWQiLAogICAgICAgICAgICAgIHN1ZmZpeCA9IGMoIi5hcm0iLCAiLmdlbmUiKSkKfSBlbHNlIHsKICBjb21iaW5lZF9zdGF0dXNfY291bnRfZGYgPC0gYXJtX3N0YXR1c19jb3VudAp9CgojIERpc3BsYXkgY29tYmluZWQgc3RhdHVzIGNvdW50cyB0YWJsZQpjb21iaW5lZF9zdGF0dXNfY291bnRfZGYKYGBgCgojIyBUcmFuc2Zvcm0gZGF0YSBpbnRvIGxvbmcgZm9ybWF0CgojIyMgVHJhbmZvcm0gdGhlIGNocm9tb3NvbWUgYXJtIHN0YXR1cyBkYXRhCgpgYGB7cn0KaWYgKCEocnVubmluZ19pbl9jaSkpIHsKICBmaW5hbF9hcm1fc3RhdHVzX2RmIDwtIGNvbWJpbmVkX3N0YXR1c19jb3VudF9kZiAlPiUKICAgICMgRmlsdGVyIHRvIG9ubHkgbm9uLW5ldXRyYWwgY2hyb21vc29tZSBhcm1zCiAgICBmaWx0ZXIoZG9taW5hbnRfc3RhdHVzLmFybSAhPSAibmV1dHJhbCIpICU+JQogICAgc2VsZWN0KAogICAgICBLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lEID0gYmlvc3BlY2ltZW5faWQsCiAgICAgIHJlZ2lvbiA9IGNocm9tb3NvbWVfYXJtLAogICAgICBzdGF0dXMgPSBkb21pbmFudF9zdGF0dXMuYXJtCiAgICApICU+JQogICAgZGlzdGluY3QoKQp9IGVsc2UgewogIGZpbmFsX2FybV9zdGF0dXNfZGYgPC0gY29tYmluZWRfc3RhdHVzX2NvdW50X2RmICU+JQogICAgIyBGaWx0ZXIgdG8gb25seSBub24tbmV1dHJhbCBjaHJvbW9zb21lIGFybXMKICAgIGZpbHRlcihkb21pbmFudF9zdGF0dXMgIT0gIm5ldXRyYWwiKSAlPiUKICAgIHNlbGVjdCgKICAgICAgS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCA9IGJpb3NwZWNpbWVuX2lkLAogICAgICByZWdpb24gPSBjaHJvbW9zb21lX2FybSwKICAgICAgc3RhdHVzID0gZG9taW5hbnRfc3RhdHVzCiAgICApICU+JQogICAgZGlzdGluY3QoKQp9CmBgYAoKIyMjIFRyYW5zZm9ybSB0aGUgY3l0b2JhbmQgc3RhdHVzIGRhdGEKCmBgYHtyfQppZiAoIShydW5uaW5nX2luX2NpKSkgewogIGZpbmFsX2N5dG9iYW5kX3N0YXR1c19kZiA8LSBjb21iaW5lZF9zdGF0dXNfY291bnRfZGYgJT4lCiAgICAjIEZpbHRlciB0byBvbmx5IG5ldXRyYWwgY2hyb21vc29tZSBhcm1zIGFuZCBjeXRvYmFuZHMKICAgICMgdGhhdCBhcmUgbm90IE5BCiAgICBmaWx0ZXIoZG9taW5hbnRfc3RhdHVzLmFybSA9PSAibmV1dHJhbCIsCiAgICAgICAgICAgZG9taW5hbnRfc3RhdHVzLmN5dG9iYW5kICE9ICJOQSIpICU+JQogICAgc2VsZWN0KAogICAgICBLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lEID0gYmlvc3BlY2ltZW5faWQsCiAgICAgIHJlZ2lvbiA9IGN5dG9iYW5kLAogICAgICBzdGF0dXMgPSBkb21pbmFudF9zdGF0dXMuY3l0b2JhbmQKICAgICkgJT4lCiAgICBkaXN0aW5jdCgpCn0KYGBgCgojIyMgVHJhbnNmb3JtIHRoZSBnZW5lLWxldmVsIHN0YXR1cyBkYXRhCgpgYGB7cn0KaWYgKCEocnVubmluZ19pbl9jaSkpIHsKICBmaW5hbF9nZW5lX3N0YXR1c19kZiA8LSBjb21iaW5lZF9zdGF0dXNfY291bnRfZGYgJT4lCiAgICAjIEZpbHRlciB0byBvbmx5IG5ldXRyYWwgY2hyb21vc29tZSBhcm1zIGFuZCBjeXRvYmFuZHMKICAgICMgdGhhdCBhcmUgTkEgb3IgdW5zdGFibGUgKGRvIG5vdCBoYXZlIGEgY2xlYXIgZ2FpbiBvciBsb3NzIGNhbGwpCiAgICBmaWx0ZXIoZG9taW5hbnRfc3RhdHVzLmFybSA9PSAibmV1dHJhbCIsCiAgICAgICAgICAgZG9taW5hbnRfc3RhdHVzLmN5dG9iYW5kID09IGMoIk5BIiwgInVuc3RhYmxlIikpICU+JQogICAgc2VsZWN0KAogICAgICBLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lEID0gYmlvc3BlY2ltZW5faWQsCiAgICAgIHJlZ2lvbiA9IGdlbmVfc3ltYm9sLAogICAgICBzdGF0dXMgPSBkb21pbmFudF9zdGF0dXMKICAgICkgJT4lCiAgICBkaXN0aW5jdCgpCn0KYGBgCgojIyMgQ29tYmluZSBzdGF0dXMgZGF0YSBmb3IgYWxsIHJlZ2lvbnMKCmBgYHtyfQppZiAoIShydW5uaW5nX2luX2NpKSkgewogICMgQmluZCB0aGUgcm93cyBvZiBlYWNoIHJlZ2lvbidzIGRhdGEuZnJhbWUKICBmaW5hbF9sb25nX3N0YXR1c19kZiA8LSBiaW5kX3Jvd3MobGlzdChjaHJvbW9zb21lX2FybSA9IGZpbmFsX2FybV9zdGF0dXNfZGYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN5dG9iYW5kID0gZmluYWxfY3l0b2JhbmRfc3RhdHVzX2RmLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5lX3N5bWJvbCA9IGZpbmFsX2dlbmVfc3RhdHVzX2RmKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLmlkID0gInJlZ2lvbl90eXBlIikgJT4lCiAgICBmaWx0ZXIoc3RhdHVzICE9ICJ1bmNhbGxhYmxlIikKfSBlbHNlIHsKICBmaW5hbF9sb25nX3N0YXR1c19kZiA8LSBmaW5hbF9hcm1fc3RhdHVzX2RmICU+JQogICAgZmlsdGVyKHN0YXR1cyAhPSAidW5jYWxsYWJsZSIpCn0KCiMgV3JpdGUgZmluYWwgbG9uZyBzdGF0dXMgdGFibGUgdG8gZmlsZQp3cml0ZV90c3YoZmluYWxfbG9uZ19zdGF0dXNfZGYsIGZpbGUucGF0aChyZXN1bHRzX2RpciwgImNvbnNlbnN1c19zZWdfbW9zdF9mb2NhbF9jbl9zdGF0dXMudHN2Lmd6IikpCgojIERpc3BsYXkgZmluYWwgbG9uZyBzdGF0dXMgdGFibGUKZmluYWxfbG9uZ19zdGF0dXNfZGYgJT4lCiAgYXJyYW5nZShyZWdpb25fdHlwZSkKYGBgCgojIyMgU3ByZWFkIHN0YXR1cyBkYXRhCgpgYGB7cn0KZmluYWxfd2lkZV9zdGF0dXNfZGYgPC0gZmluYWxfbG9uZ19zdGF0dXNfZGYgJT4lCiAgdGlkeXI6OnNwcmVhZChLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lELCBzdGF0dXMpCgojIERpc3BsYXkgc3RhdHVzIGRhdGEgc3ByZWFkIGFjcm9zcyBzYW1wbGVzIGluIHdpZGUgZm9ybWF0IC0tIGVhY2ggcm93IGlzIGEKIyB1bmlxdWUgcmVnaW9uCmZpbmFsX3dpZGVfc3RhdHVzX2RmCmBgYAoKCiMjIFNlc3Npb24gSW5mbwoKYGBge3J9CnNlc3Npb25JbmZvKCkKYGBgCg==